package service

import (
	"admin/config"
	rbacmodel "admin/models/auth"
	"context"
	"github.com/go-oauth2/oauth2/v4/errors"
	"github.com/go-oauth2/oauth2/v4/generates"
	"github.com/go-oauth2/oauth2/v4/manage"
	"github.com/go-oauth2/oauth2/v4/models"
	oauth2server "github.com/go-oauth2/oauth2/v4/server"
	"github.com/go-oauth2/oauth2/v4/store"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"net/http"
	"strconv"
	"time"
)

// InitOAuth2 初始化 OAuth2 Server
// db: 数据库实例
// cfg: OAuth2 配置
// 返回值: OAuth2 服务器实例和错误信息
func InitOAuth2(db *gorm.DB, cfg *config.OAuth2Config) (*oauth2server.Server, error) {
	// 创建默认的OAuth2管理器
	manager := manage.NewDefaultManager()

	// 内存 token
	// 设置令牌存储方式为内存存储
	manager.MustTokenStorage(store.NewMemoryTokenStore())
	// 设置访问令牌生成器
	manager.MapAccessGenerate(generates.NewAccessGenerate())

	// client store
	// 创建客户端存储
	clientStore := store.NewClientStore()
	// 从配置文件中读取客户端信息（客户端ID、密钥、域名）
	_ = clientStore.Set(cfg.ClientID, &models.Client{
		ID:     cfg.ClientID,
		Secret: cfg.ClientSecret,
		Domain: cfg.ClientDomain,
	})
	// 将客户端存储映射到管理器
	manager.MapClientStorage(clientStore)

	// 设置 token 生命周期
	// 配置密码令牌的生命周期参数
	manager.SetPasswordTokenCfg(&manage.Config{
		AccessTokenExp:    time.Hour * 2,      // 访问令牌有效期2小时
		RefreshTokenExp:   time.Hour * 24 * 3, // 刷新令牌有效期3天
		IsGenerateRefresh: true,               // 启用刷新令牌生成
	})

	// 创建OAuth2服务器实例
	srv := oauth2server.NewDefaultServer(manager)
	// 允许通过GET请求获取访问令牌
	srv.SetAllowGetAccessRequest(true)
	// 设置客户端信息处理方式为表单处理
	srv.SetClientInfoHandler(oauth2server.ClientFormHandler)

	// password grant handler
	// 设置密码授权处理函数
	srv.SetPasswordAuthorizationHandler(func(ctx context.Context, clientID, username, password string) (string, error) {
		// 根据用户名查找用户
		var u rbacmodel.AdminUser
		if err := db.Where("username = ?", username).First(&u).Error; err != nil {
			return "", errors.ErrInvalidGrant
		}
		// 验证用户密码
		if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)); err != nil {
			return "", errors.ErrInvalidGrant
		}
		// 返回用户ID作为标识
		return strconv.FormatUint(uint64(u.ID), 10), nil
	})

	return srv, nil
}

// HandleTokenRequest 处理 Token 请求
func HandleTokenRequest(srv *oauth2server.Server, w http.ResponseWriter, r *http.Request) error {
	return srv.HandleTokenRequest(w, r)
}
